#ifndef __SG_RING_BUFFER_BYTES__
#define __SG_RING_BUFFER_BYTES__

#include <vector>
#include <string>
#include <memory.h>

// https://www.cnblogs.com/l00l/p/4115001.html
// only one thread read and one thread write.
class RingBufferBytes
{
    public:
        RingBufferBytes(int size = 4096):
            bufferSize(size),
            head(0),
            tail(0),
            isClosed(false)
        {
            buffer = new char[bufferSize];
        };
        virtual ~RingBufferBytes()
        {
            delete[] buffer;
        };
        int write(const char* data, int len) {
            if(isClosed) return -1;
            if (full()) {
                return 0;
            }
            int ret_size = 0;
            int left_size = get_left_size();
            int capa_size = bufferSize - tail;
            if(left_size <= capa_size) {
                if(len <= left_size) {
                    ret_size = len;
                }else{
                    ret_size = left_size;
                }
                memcpy(buffer+tail, data, ret_size);
                tail = (tail + ret_size) % bufferSize;
            }else{
                if(len <= capa_size) {
                    ret_size = len;
                    memcpy(buffer+tail, data, ret_size);
                    tail = (tail + ret_size) % bufferSize;
                }else{
                    memcpy(buffer+tail, data, capa_size);
                    tail = (tail + capa_size) % bufferSize;
                    if(len <= left_size) {
                        ret_size = len;
                    }else{
                        ret_size = left_size;
                    }
                    memcpy(buffer+tail, data+capa_size, ret_size-capa_size);
                    tail = (tail + ret_size - capa_size) % bufferSize;
                }
            }
            return ret_size;
        };
        int read(char* data, int len) {
            if (isClosed && empty()) {
                return -1;
            }
            if (empty()) {
                return 0;
            }
            int ret_size = 0;
            int cont_size = bufferSize - get_left_size() - 1;
            int capa_size = bufferSize - head;
            if(cont_size <= capa_size) {
                if(len <= cont_size) {
                    ret_size = len;
                }else{
                    ret_size = cont_size;
                }
                memcpy(data, buffer+head, ret_size);
                head = (head + ret_size) % bufferSize;
            }else{
                if(len <= capa_size) {
                    ret_size = len;
                    memcpy(data, buffer+head, ret_size);
                    head = (head + ret_size) % bufferSize;
                }else{
                    memcpy(data, buffer+head, capa_size);
                    head = (head + capa_size) % bufferSize;
                    if(len <= cont_size) {
                        ret_size = len;
                    }else{
                        ret_size = cont_size;
                    }
                    memcpy(data+capa_size, buffer+head, ret_size-capa_size);
                    head = (head + ret_size - capa_size) % bufferSize;
                }
            }
            return ret_size;
        };
        int read_chunk(char* data, int len) {
            if (isClosed && empty()) {
                return -1;
            }
            if (empty()) {
                return 0;
            }
            // note : first juge close then get cont !!!
            if(!isClosed)
            {
                int cont_size = bufferSize - get_left_size() - 1;
                if (len > cont_size) {
                    return 0;
                }
            }
            int ret_size = 0;
            int cont_size = bufferSize - get_left_size() - 1;
            int capa_size = bufferSize - head;
            if(cont_size <= capa_size) {
                if(len <= cont_size) {
                    ret_size = len;
                }else{
                    ret_size = cont_size;
                }
                memcpy(data, buffer+head, ret_size);
                head = (head + ret_size) % bufferSize;
            }else{
                if(len <= capa_size) {
                    ret_size = len;
                    memcpy(data, buffer+head, ret_size);
                    head = (head + ret_size) % bufferSize;
                }else{
                    memcpy(data, buffer+head, capa_size);
                    head = (head + capa_size) % bufferSize;
                    if(len <= cont_size) {
                        ret_size = len;
                    }else{
                        ret_size = cont_size;
                    }
                    memcpy(data+capa_size, buffer+head, ret_size-capa_size);
                    head = (head + ret_size - capa_size) % bufferSize;
                }
            }
            return ret_size;
        };
        void open(){
            isClosed = false;
        }
        void close(){
            isClosed = true;
        }
        //std::vector<std::string> getAll() {
            //std::vector<std::string> result;
            //if (empty()) {
                //return result;
            //}
            //int copyTail = tail;
            //int cnt = head < copyTail ? copyTail - head : bufferSize - head + copyTail;
            //result.reserve(cnt);
            //if (head < copyTail) {
                //for (int i = head; i < copyTail; i++) {
                    //result[i - head] = buffer[i];
                //}
            //} else {
                //for (int i = head; i < bufferSize; i++) {
                    //result[i - head] = buffer[i];
                //}
                //for (int i = 0; i < copyTail; i++) {
                    //result[bufferSize - head + i] = buffer[i];
                //}
            //}
            //head = copyTail;
            //return result;
        //};

    private:
        int bufferSize;
        volatile int head;
        volatile int tail;
        volatile bool isClosed;
        char* buffer;

    private:
        bool empty() {
            return head == tail;
        };
        bool full() {
            return (tail + 1) % bufferSize == head;
        };
        int get_left_size()
        {
            if(tail >= head)
            {
                return bufferSize - tail + head -1;
            }else{
                return head - tail -1;
            }
        };
};


#endif

